/**************************************************************************/
/* GENERR - program for error messages generation                         */
/* (c) Petr Peringer 1991-1997                                            */
/**************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

/**************************************************************************/

#define  MAXERR      1000
#define  MAXIDLEN    32
#define  MAXLINELEN  255

enum Boolean { FALSE, TRUE };

char tittle[] =
    "GENERR - error messages generator                     (c)PerPet 1991-1997 \n";
char help[] =
    "\n"
    " usage:   GENERR InputFileName \n"
    "\n"
    " Input file format:\n"
    "\n"
    "   ErrorFunctionName(ErrorEnumName)\n"
    "   ErrorName0      Text of error message 0\n"
    "   ErrorName1      Text of error message 1\n"
    "    ...\n"
    "\n"
    "   Comment line starts with nonletter character\n"
    "   Name must be an C-identifier!\n"
    "\n"
    "   Output files are:\n"
    "     InputFileName.h\n" "     InputFileName.cc\n";

struct tabitem {
    char *ErrId;
    char *ErrMsg;
};

typedef struct {
    struct tabitem Table[MAXERR];
    unsigned NumErr;
} TABLE;

TABLE *tab;
char funcname[MAXIDLEN + 1];
char enumname[MAXIDLEN + 1];

FILE *inf;
char fname[512];                /* module file name */
char modfname[80];              /* module file name */
char headfname[80];             /* header file name */
char line[MAXLINELEN + 2];
char *sp = line;
int lineno = 0;

/**************************************************************************/
/**************************************************************************/
/* Generated by program GENERR */

enum ErrEnum {
/* 0 */ NoError,
/* 1 */ ErrBadArguments,
/* 2 */ ErrBadFile,
/* 3 */ ErrBadHeader,
/* 4 */ ErrBadIdentifier,
/* 5 */ ErrCantOpenIn,
/* 6 */ ErrCantOpenOut,
/* 7 */ ErrDuplicateId,
/* 8 */ ErrIdTooLong,
/* 9 */ ErrLineTooLong,
/* 10 */ ErrNoIdentifier,
/* 11 */ ErrNoMemory,
/* 12 */ ErrTooManyMsg,
};

static char _Errors[] = {
/* 0 */ "\0"
/* 1 */ "Bad arguments\0"
/* 2 */ "File have not a header\0"
/* 3 */ "Error in head line\0"
/* 4 */ "Bad identifier\0"
/* 5 */ "Can't open input file\0"
/* 6 */ "Can't open output file\0"
/* 7 */ "Duplicate identifier\0"
/* 8 */ "Identifier too long\0"
/* 9 */ "Line too long\0"
/* 10 */ "Identifier expected\0"
/* 11 */ "Out of memory\0"
/* 12 */ "Too many messages in input file\0"
};

char *ErrMsg(enum ErrEnum N)
{
    char *p = _Errors;
    while (N-- > 0)
        while (*p++ != '\0');
    return p;
};

/**************************************************************************/
/**************************************************************************/
void error(enum ErrEnum N)
{
    printf("Error#%d: %s\n", N, ErrMsg(N));
    if (N == ErrBadArguments)
        printf(help);
    exit(N);
}

/**************************************************************************/
void InitTable(TABLE ** t)
{
    (*t) = malloc(sizeof(TABLE));
    if (*t == NULL)
        error(ErrNoMemory);
    (*t)->NumErr = 0;
}


FILE *OpenInputFile(char *name)
{
    FILE *inf;
    char *s;
    strncpy(fname, name, 500);  /* save name for future use */
    inf = fopen(name, "rt");
    if (inf == NULL)
        error(ErrCantOpenIn);
    strcpy(modfname, name);
    for (s = modfname; *s != '\0' && *s != '.'; s++);
    strcpy(s, ".cc");
    strcpy(headfname, name);
    for (s = headfname; *s != '\0' && *s != '.'; s++);
    strcpy(s, ".h");
    return inf;
}

FILE *OpenOutputFile(char *name)
{
    FILE *outf;
    outf = fopen(name, "wt");
    if (outf == NULL)
        error(ErrCantOpenOut);
    return outf;
}


/**************************************************************************/
int GetLine(FILE * inf)
{                               /* read one input line to buffer */
    int l;
    do {
        if (fgets(line, MAXLINELEN, inf) == NULL)
            return FALSE;
        ++lineno;
#ifdef PRINT_LINE_NUMBERS
        if (lineno == 1)
            printf("Line start ");
        printf("\b\b\b\b\b\b%-5d ", lineno);
#endif
        l = strlen(line) - 1;
        if (line[l] != '\n') {
            if (!feof(inf))
                error(ErrLineTooLong);  //###
        }
        line[l] = '\0';
    } while (!(isalpha(line[0]) || line[0] == '_') || line[0] == '\0');
    sp = line;
    return TRUE;
}

/**************************************************************************/
void SkipBlanks(void)
{
    while (*sp == ' ' || *sp == '\t')
        sp++;
}

int GetChar(void)
{
/***** get character *****/
    return (*sp == '\0') ? *sp : *sp++;
}

void GetIdent(char *sptr)
{
/***** get an identifier *****/
    int i = 0;
    if (!(isalpha(*sp) || *sp == '_'))
        error(ErrBadIdentifier);
    while (i <= MAXIDLEN && (isalnum(*sp) || *sp == '_'))
        sptr[i++] = *sp++;
    if (i > MAXIDLEN)
        error(ErrIdTooLong);
    if (i == 0)
        error(ErrBadIdentifier);
    sptr[i] = '\0';
}

char *MyStrDup(char *sp)
{
    char *s = strdup(sp);
    if (s == NULL)
        error(ErrNoMemory);     /* with test */
    return s;
}

char *GetErrId(void)
{
    char s[MAXIDLEN + 1];
    GetIdent(s);
    return MyStrDup(s);
}

char *GetErrMsg(void)
{
    SkipBlanks();
    return MyStrDup(sp);
}

/**************************************************************************/
/*                             G E T                                      */
/**************************************************************************/
void GetHeader(FILE * inf, char *funcname, char *enumname)
{
    if (!GetLine(inf))
        error(ErrBadFile);
    GetIdent(funcname);
    SkipBlanks();
    if (GetChar() != '(')
        error(ErrBadHeader);
    SkipBlanks();
    GetIdent(enumname);
    SkipBlanks();
    if (GetChar() != ')')
        error(ErrBadHeader);
}

int CheckUniq(TABLE * t)
{
    int i;
    char *s = t->Table[t->NumErr].ErrId;
    for (i = 0; i < t->NumErr; i++)
        if (strcmp(t->Table[i].ErrId, s) == 0)
            return FALSE;
    return TRUE;                /* unique identifier */
}

void GetTable(FILE * inf, TABLE * t)
{
    while (GetLine(inf)) {
        t->Table[t->NumErr].ErrId = GetErrId();
        t->Table[t->NumErr].ErrMsg = GetErrMsg();
        if (!CheckUniq(t))
            error(ErrDuplicateId);
        if (++t->NumErr > MAXERR)
            error(ErrTooManyMsg);
    }
}

/**************************************************************************/
/*                             G E N E R                                  */
/**************************************************************************/
void GenEnum(FILE * outf, TABLE * t)
{
    int i;
    fprintf(outf, "enum %s {\n", enumname);
    for (i = 0; i < (*t).NumErr; i++)
        fprintf(outf, "/* %d */ %s,\n", i, (*t).Table[i].ErrId);
    fprintf(outf, "};\n\n");
}

void GenMessages(FILE * outf, TABLE * t)
{
    int i;
    fprintf(outf, "static char _Errors[] = {\n");
    for (i = 0; i < (*t).NumErr; i++)
        fprintf(outf, "/* %d */ \"%s\\0\" \n", i, (*t).Table[i].ErrMsg);
    fprintf(outf, "};\n\n");
}

void GenErrFun(FILE * outf)
{
    fprintf(outf, "char *%s(enum %s N)\n", funcname, enumname);
    fprintf(outf, "{\n");
    fprintf(outf, "  char *p = _Errors;\n");
    fprintf(outf, "  int i = N;\n");
    fprintf(outf, "  while( i-- > 0 )\n");
    fprintf(outf, "    while( *p++ != '\\0' ) { /*empty*/ }\n");
    fprintf(outf, "  return p;\n");
    fprintf(outf, "};\n");
}

/**************************************************************************/
void GenerModule(TABLE * t)
{
    FILE *outf = OpenOutputFile(modfname);
    fprintf(outf, "//\n// %s\n", modfname);
    fprintf(outf, "//\n//\n//\n//\n");
    fprintf(outf, "/* Generated from file '%s' by program GENERR */\n\n",
            fname);
    fprintf(outf, "#include \"simlib.h\"\n");
    fprintf(outf, "namespace simlib3 {\n");
    fprintf(outf, "#include \"%s\"\n\n", headfname);
    GenMessages(outf, t);
    GenErrFun(outf);
    fprintf(outf, "} // namespace simlib3 \n\n");
    fclose(outf);
}

/**************************************************************************/
void GenerHeader(TABLE * t)
{
    FILE *outf = OpenOutputFile(headfname);
    fprintf(outf, "//\n// %s\n", headfname);
    fprintf(outf, "//\n//\n//\n//\n");
    fprintf(outf, "/* Generated from file '%s' by program GENERR */\n\n",
            fname);
    GenEnum(outf, t);
    fprintf(outf, "extern char *%s(enum %s N);\n", funcname, enumname);
    fprintf(outf, "\n");
    fclose(outf);
}

/**************************************************************************/
/*                                M A I N                                 */
/**************************************************************************/
int main(int argc, char *argv[])
{
    printf(tittle);
    if (argc != 2)
        error(ErrBadArguments);
    inf = OpenInputFile(argv[1]);
    InitTable(&tab);
    GetHeader(inf, funcname, enumname); /* cte 1 radek - hlavicku          */
    GetTable(inf, tab);         /* cte chybova hlaseni do tabulky  */
    GenerModule(tab);           /* generuje vyst. soubor .C        */
    GenerHeader(tab);           /* generuje vyst. soubor .H        */
    printf("\nNumber of messages = %d \n", tab->NumErr);
    return (0);                 /* OK                              */
}

/**************************************************************************/
